默认布局:头部组件结构+基础组件样式扩展
Header 组件是布局右侧的核心工具栏,集成了折叠按钮、面包屑、国际化切换、暗黑模式、全屏、主题设置、头像菜单等多个功能模块。本节重点实现 Header 的基础结构、国际化下拉组件(ChangeLocale),以及如何通过 extends 和 computed 为基础组件扩展样式配置能力。
Header 组件的基础结构
Header 使用 Flex 布局,左侧是折叠按钮,右侧是一组工具组件:
<!-- src/components/layouts/header.vue -->
<template>
<div class="flex items-center px-4 h-12 border-b">
<!-- 左侧:折叠按钮 -->
<Iconify
:icon="collapse ? 'ep:expand' : 'ep:fold'"
class="text-xl cursor-pointer"
@click="collapse = !collapse"
/>
<!-- 中间撑满 -->
<div class="flex-grow" />
<!-- 右侧工具组 -->
<div class="flex items-center gap-2">
<ChangeLocale :locales="locales" />
<DarkModeToggle />
<FullScreen />
<ThemeSettings />
<AvatarMenu />
</div>
</div>
</template>
vue
折叠按钮使用 Iconify 组件,通过 collapse 的值切换 expand 和 fold 图标。点击时通过 v-model 更新折叠状态。
ChangeLocale:国际化下拉组件
国际化组件使用 Element Plus 的 el-dropdown 实现,支持点击触发语言切换。
接口设计
// src/components/layouts/change-locale/types.ts
export interface LocaleItem {
text: string // 显示文本,如 "中文"
icon?: string // Iconify 图标名称或字符串
name: string // 语言文件名,如 "zh-CN"
}
typescript
组件实现
<!-- src/components/layouts/change-locale.vue -->
<template>
<el-dropdown trigger="click" @command="handleCommand">
<slot name="header">
<Iconify icon="mdi:translate" class="text-xl" />
</slot>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
v-for="(locale, index) in locales"
:key="index"
:command="locale.name"
>
<div class="flex items-center">
<Iconify
v-if="locale.icon"
:icon="locale.icon"
class="mr-2"
/>
{{ locale.text }}
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
vue
渲染图标的三种方式
在 el-dropdown-item 中渲染图标有三种实现方式:
方式一:JSX 渲染函数
const dropdownIcon = (icon: string) => (
<Iconify icon={icon} />
)
typescript
方式二:h 函数
import { h } from 'vue'
const dropdownIcon = (icon: string) =>
h(Iconify, { icon })
typescript
方式三:Slot 模板(推荐)
直接在模板中使用 <Iconify :icon="locale.icon" />,配合 v-if 控制显示。这种方式最直观,不引入 JSX 依赖。
command 回调
const emit = defineEmits<{
change: [name: string]
}>()
const handleCommand = (command: string) => {
emit('change', command)
}
typescript
父组件(Header)接收 change 事件,调用 loadLocaleMessages 动态加载语言包:
const handleChangeLocale = async (name: string) => {
await loadLocaleMessages(name)
locale.value = name
}
typescript
基础组件样式扩展
Header 中的多个组件(ChangeLocale、DarkModeToggle、FullScreen)都涉及图标样式配置。为了避免每个组件都重复定义图标大小、颜色等属性,采用两种扩展策略:
策略一:iconClass prop
interface ChangeLocaleProps {
locales: LocaleItem[]
iconClass?: string
}
withDefaults(defineProps<ChangeLocaleProps>(), {
iconClass: 'text-xl',
})
typescript
组件内部直接绑定 class:
<Iconify :class="iconClass" />
vue
策略二:extends IconProps
import type { IconProps } from '~/components/icon/iconify.vue'
interface ChangeLocaleProps extends /* IconProps */ {
locales: LocaleItem[]
}
typescript
使用 computed 将 props 解构为 iconProps 和 restProps:
const { icon, class: iconClass, ...restProps } = props
const iconProps = computed(() => restProps)
typescript
这样用户可以传入 Iconify 组件的任意属性(fontSize、color、rotate 等),组件自动透传。
样式对齐处理
Header 右侧的工具组件需要垂直居中并保持统一间距:
/* 统一使用 Tailwind 类 */
.flex.items-center.gap-2
css
每个图标组件的默认大小设为 text-xl(20px),确保视觉统一。如果觉得太大,可以统一调整为 text-lg(18px)。
本节小结
- Header 结构:Flex 布局,左侧折叠按钮,中间弹性空间,右侧工具组件组。
- 国际化组件:
el-dropdown+command事件,支持动态加载语言包。 - 样式扩展:通过
extends或iconClassprop 为基础组件提供图标样式配置能力。 - Slot 自定义:ChangeLocale 提供
header插槽,允许用户自定义触发图标。
↑